#!/usr/bin/python
import roslib; roslib.load_manifest('speech_recognition')
import rospy
from std_msgs.msg import String
from geometry_msgs.msg import Point


import pyaudio
import subprocess
import xmlrpclib
import sys
import time
import os


LENGTH_IN_MIN = float(sys.argv[1])
SERVER_URL = "http://10.68.0.150:8000"
SERVER = xmlrpclib.Server(SERVER_URL)

CHUNK_SIZE=16000
RATE = 16000
FORMAT = pyaudio.paInt16
DEVICE_NAME = "Kinect"
NUM_CHANNELS = 1
INTERLEAVED = True

################################################################################
# Stream Handling
################################################################################

def send_chunk(data):
    irrelevant_thing = SERVER.take_chunk(xmlrpclib.Binary(data))

class StreamManager:
    def start(self):
        with suppress_stdout_stderr():
            self.p = pyaudio.PyAudio()
        device_index = None

        # Find the right audio device
        for device_num in range(self.p.get_device_count()):
            device_info = self.p.get_device_info_by_index(device_num)
            if DEVICE_NAME in device_info['name']:
                device_index = device_num
                self.num_channels = device_info['maxInputChannels']
                print "Using device", device_info['name'],
                print "Number of channels:",self.num_channels

        self.stream = self.p.open(format=FORMAT, channels=self.num_channels,
                                  rate=RATE, input=True, output=True,
                                  input_device_index=device_index,
                                  frames_per_buffer=CHUNK_SIZE)

    def read(self):
        return self.stream.read(CHUNK_SIZE)

    def stop(self):
        self.stream.close()
        self.p.terminate()

class suppress_stdout_stderr(object):
    '''
    A context manager for doing a "deep suppression" of stdout and stderr in 
    Python, i.e. will suppress all print, even if the print originates in a 
    compiled C/Fortran sub-function.
       This will not suppress raised exceptions, since exceptions are printed
    to stderr just before a script exits, and after the context manager has
    exited (at least, I think that is why it lets exceptions through).      

    '''
    def __init__(self):
        # Open a pair of null files
        self.null_fds =  [os.open(os.devnull,os.O_RDWR) for x in range(2)]
        # Save the actual stdout (1) and stderr (2) file descriptors.
        self.save_fds = (os.dup(1), os.dup(2))

    def __enter__(self):
        # Assign the null pointers to stdout and stderr.
        os.dup2(self.null_fds[0],1)
        os.dup2(self.null_fds[1],2)

    def __exit__(self, *_):
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0],1)
        os.dup2(self.save_fds[1],2)
        # Close the null files
        os.close(self.null_fds[0])
        os.close(self.null_fds[1])
    
################################################################################
# Speech Recording
################################################################################

class SpeechRecorder:
    def __init__(self):
        self.sm = StreamManager()
        
    def record(self):
        end_time = time.time()+LENGTH_IN_MIN*60
        self.sm.start()
        irrelevant = SERVER.start(self.sm.num_channels, INTERLEAVED,
                                  self.sm.p.get_sample_size(FORMAT),
                                  RATE, CHUNK_SIZE)
        while time.time() < end_time:
            print "streaming audio"
            send_chunk(self.sm.read())

        self.sm.stop()
        irrelevant = SERVER.close()
    
################################################################################
# Main Loop
################################################################################

if __name__ == '__main__':
    speech_recorder = SpeechRecorder()
    while not rospy.is_shutdown():
        speech_recorder.record()
        sys.exit()
